package Merlion.SCM.bean;

import Merlion.CRM.bean.MessageManagerBeanRemote;
import Merlion.CRM.entity.BillingAddressEntity;
import Merlion.CRM.entity.ContactEntity;
import Merlion.CRM.entity.CustomerEntity;
import Merlion.CRM.entity.DeliveryOrderEntity;
import Merlion.CRM.entity.LineEntryEntity;
import Merlion.CRM.entity.SalesDocumentEntity;
import Merlion.CRM.entity.SalesOrderEntity;
import Merlion.CRM.entity.ShippingAddressEntity;
import Merlion.MRP.bean.ProductionManagementBeanRemote;
import Merlion.MRP.entity.IndividualWeeklyMaterialRequirementEntity;
import Merlion.MRP.entity.ReservationEntity;
import Merlion.SCM.entity.InventoryBatchEntity;
import Merlion.SCM.entity.InventoryEntity;
import Merlion.SCM.entity.InventoryMovementsRecordEntity;
import Merlion.SCM.entity.MaterialEntity;
import Merlion.SCM.entity.PickingOrderEntity;
import Merlion.SCM.entity.PickingOrderEntryEntity;
import Merlion.SCM.entity.ProductEntity;
import Merlion.SCM.entity.WarehouseEntity;
import Merlion.util.state.DeliveryOrderState;
import Merlion.util.state.InventoryMovementState;
import Merlion.util.state.InventoryState;
import Merlion.util.state.LineEntryState;
import Merlion.util.state.PickingOrderEntryState;
import Merlion.util.state.PickingOrderState;
import Merlion.util.state.ReservationState;
import Merlion.util.state.WarehouseInventoryState;
import Merlion.util.state.WarehouseState;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class InventoryMovementsManagementBean implements InventoryMovementsManagementBeanRemote {

    @PersistenceContext
    EntityManager em;
    @EJB
    MessageManagerBeanRemote messenger;
    @EJB
    ProductionManagementBeanRemote pmb;
    /*
    @Override
    public ReservationEntity createReservation(Long salesDocumentId) {
    SalesDocumentEntity sd = em.find(SalesDocumentEntity.class, salesDocumentId);
    
    if (sd == null) {
    throw new NullPointerException("No such sales document.");
    }
    
    ReservationEntity reservation = new ReservationEntity(sd);
    em.persist(reservation);
    for (LineEntryEntity entry : sd.getLineEntryList()) {
    ProductEntity product = entry.getProduct();
    int quantity = entry.getQuantity();
    product.setReservedQty(product.getReservedQty() + quantity);
    }       
    reservation.setSalesDoc(sd);
    sd.setReservationEntity(reservation);
    em.flush();
    return reservation;
    
    }
    
    @Override
    public void withdrawReservation(Long salesDocumentId) {
    SalesDocumentEntity sd = em.find(SalesDocumentEntity.class, salesDocumentId);
    
    if (sd == null) {
    throw new NullPointerException("No such sales document.");
    }
    
    ReservationEntity reservation = sd.getReservationEntity();
    
    if (reservation == null) {
    throw new NullPointerException("No reservation has been made.");
    }
    
    for (LineEntryEntity entry : sd.getLineEntryList()) {
    ProductEntity product = entry.getProduct();
    int quantity = entry.getQuantity();
    product.setReservedQty(product.getReservedQty() - quantity);
    }
    
    reservation.setSalesDoc(null);
    sd.setReserved(false);
    sd.setReservationEntity(null);
    em.remove(reservation);
    em.flush();
    
    }
     */

    @Override
    public void executeWeeklyIndividualRequirement(Integer yearInt, Integer monthInt, Integer weekOfMonth, Date receiptDate, String remarks) {
        System.out.println("Executing weekly individual requirements");
        Query q = em.createQuery("SELECT a FROM IndividualWeeklyMaterialRequirementEntity a WHERE a.yearInt = :yearInt AND a.monthInt = :monthInt AND a.weekOfMonth = :weekOfMonth");
        q.setParameter("yearInt", yearInt);
        q.setParameter("monthInt", monthInt);
        q.setParameter("weekOfMonth", weekOfMonth);

        for (Object o : q.getResultList()) {
            IndividualWeeklyMaterialRequirementEntity imr = (IndividualWeeklyMaterialRequirementEntity) o;
            if (!imr.isPurchased()) {
                if (imr.getRequiredAmount() != 0) {
                    System.out.println("Creating material batch for " + imr.getMaterial().toString()+ ", requirement id: " + imr.getId());
                    this.createMaterialBatch(imr.getMaterial().getId(), imr.getRequiredAmount(), receiptDate, remarks);
                }
                imr.setPurchased(true);
                em.flush();
            } else {
                throw new IllegalStateException("The required material has been purchased previously");
            }

        }
    }

    protected Long createMaterialBatch(Long materialId, int quantity, Date receiptDate, String remarks) {
        System.out.println("Creating Material Batch Record");

        MaterialEntity material = em.find(MaterialEntity.class, materialId);

        if (material == null) {
            throw new NullPointerException("No such material exists");
        }

        InventoryBatchEntity batch = new InventoryBatchEntity();

        batch.createMaterialBatch(material, receiptDate, quantity, quantity, 0, remarks, receiptDate);

        em.persist(batch);
        em.flush();

        return batch.getId();
    }

    @Override
    public void addInventoryBatchToWarehouse(Long batchId, Long warehouseId) {
        System.out.println("Putting Material Batch to Warehouse");
        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);
        InventoryMovementsRecordEntity log = new InventoryMovementsRecordEntity();

        if (batch == null) {
            throw new NullPointerException();
        }

        if (warehouse == null) {
            throw new NullPointerException();
        }

        if (batch.getItem().getInventoryType().equals("RAW")) {
            MaterialEntity material = batch.getMaterial();

            batch.setWarehouse(warehouse);

            material.setInHandQty(material.getInHandQty() + batch.getInHandQty());
            material.setReservedQty(material.getReservedQty() + batch.getReservedQty());

            int total = batch.getInHandQty() + batch.getReservedQty();

            List<InventoryBatchEntity> batches = new ArrayList();
            batches = warehouse.getInventoryBatchEntities();
            batches.add(batch);
            warehouse.setInventoryBatchEntities(batches);

            log.createLog(batch.getItemId(), batch.getItem().getName(), total, "INBOUND", warehouseId, warehouse.getName());
            em.persist(log);

            em.flush();
            System.out.println("Material is successfully assigned to a warehouse");
        } else if (batch.getItem().getInventoryType().equals("FIN")) {

            ProductEntity product = batch.getProduct();

            batch.setWarehouse(warehouse);

            product.setInHandQty(product.getInHandQty() + batch.getInHandQty());
            product.setReservedQty(product.getReservedQty() + batch.getReservedQty());

            int total = batch.getInHandQty() + batch.getReservedQty();

            List<InventoryBatchEntity> batches = new ArrayList();
            batches = warehouse.getInventoryBatchEntities();
            batches.add(batch);
            warehouse.setInventoryBatchEntities(batches);

            log.createLog(batch.getItemId(), batch.getItem().getName(), total, "INBOUND", warehouseId, warehouse.getName());
            em.persist(log);

            em.flush();
            System.out.println("Product is successfully assigned to a warehouse");

        }

    }

    @Override
    public void addMaterialBatchToWarehouse(Long batchId, Long warehouseId) {

        System.out.println("Putting Material Batch to Warehouse");
        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);

        if (batch == null) {
            throw new NullPointerException();
        }

        if (warehouse == null) {
            throw new NullPointerException();
        }
        MaterialEntity material = batch.getMaterial();

        batch.setWarehouse(warehouse);

        material.setInHandQty(material.getInHandQty() + batch.getInHandQty());
        material.setReservedQty(material.getReservedQty() + batch.getReservedQty());

        List<InventoryBatchEntity> batches = new ArrayList();
        batches = warehouse.getInventoryBatchEntities();
        batches.add(batch);
        warehouse.setInventoryBatchEntities(batches);

        em.flush();
    }

    @Override
    public void addProductBatchToWarehouse(Long batchId, Long warehouseId) {

        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);

        if (batch == null) {
            throw new NullPointerException();
        }

        if (warehouse == null) {
            throw new NullPointerException();
        }
        ProductEntity product = batch.getProduct();

        batch.setWarehouse(warehouse);

        product.setInHandQty(product.getInHandQty() + batch.getInHandQty());
        product.setReservedQty(product.getReservedQty() + batch.getReservedQty());

        List<InventoryBatchEntity> batches = new ArrayList();
        batches = warehouse.getInventoryBatchEntities();
        batches.add(batch);
        warehouse.setInventoryBatchEntities(batches);

        em.flush();

    }

    @Override
    public void discardProductBatchFromWarehouse(Long batchId, Long warehouseId, String remarks) {

        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);

        if (batch == null) {
            throw new NullPointerException();
        }

        if (warehouse == null) {
            throw new NullPointerException();
        }

        if (batch.getReservedQty() > 0) {
            List<ReservationEntity> reservations = new ArrayList();
            reservations = batch.getReservationEntities();
            String reservationIds = "Reservation IDs: ";
            for (ReservationEntity reservation : reservations) {
                reservationIds.concat(reservation.getId().toString()).concat(", ");
            }
            throw new IllegalStateException("Cannot withdraw reserved batch. Withdraw reservations for the following items: "
                    + reservationIds + "first");
        }

        ProductEntity product = batch.getProduct();

        product.setInHandQty(product.getInHandQty() - batch.getInHandQty());

        batch.setDeleted(true);

        List<InventoryBatchEntity> batches = new ArrayList();
        batches = warehouse.getInventoryBatchEntities();
        batches.remove(batch);
        warehouse.setInventoryBatchEntities(batches);

        em.flush();
    }

    @Override
    public void discardMaterialBatchFromWarehouse(Long batchId, Long warehouseId, String remarks) {

        InventoryBatchEntity batch = em.find(InventoryBatchEntity.class, batchId);
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);

        if (batch == null) {
            throw new NullPointerException();
        }

        if (warehouse == null) {
            throw new NullPointerException();
        }

        MaterialEntity material = batch.getMaterial();

        material.setInHandQty(material.getInHandQty() - batch.getInHandQty());

        batch.setDeleted(true);

        List<InventoryBatchEntity> batches = new ArrayList();
        batches = warehouse.getInventoryBatchEntities();
        batches.remove(batch);
        warehouse.setInventoryBatchEntities(batches);

        em.flush();
    }

    @Override
    public List<InventoryState> viewUnhousedInventory() {
        System.out.println("View Unhoused Inventory");
        Query q = em.createQuery("SELECT a FROM InventoryBatchEntity a where a.warehouse IS NULL");

        List<InventoryState> states = new ArrayList();

        for (Object o : q.getResultList()) {
            InventoryBatchEntity batch = (InventoryBatchEntity) o;
            InventoryState state = new InventoryState();
            InventoryEntity item = batch.getItem();
            state.createInventoryBatchState(batch.getId(), item.getId(), item.getName(), item.getType(), batch.getInHandQty(), batch.getReservedQty());
            states.add(state);
        }

        return states;
    }

    @Override
    public WarehouseInventoryState viewWarehouseInventory(long warehouseId) {
        WarehouseEntity warehouse = em.find(WarehouseEntity.class, warehouseId);
        WarehouseState wState = new WarehouseState(warehouse.getId(), warehouse.getCapacity(),
                warehouse.getName(), warehouse.getStreet(), warehouse.getCity(), warehouse.getCountry(), warehouse.getPostcode());
        List<InventoryBatchEntity> batches = warehouse.getInventoryBatchEntities();
        List<InventoryState> products = new ArrayList();
        List<InventoryState> materials = new ArrayList();

        for (InventoryBatchEntity batch : batches) {
            InventoryEntity item = batch.getItem();

            InventoryState invState = new InventoryState();
            invState.createInventoryBatchState(batch.getId(), item.getId(), item.getName(), item.getInventoryType(), batch.getInHandQty(), batch.getReservedQty());

            if (item.getInventoryType().equals("FIN")) {
                products.add(invState);
            } else if (item.getInventoryType().equals("RAW")) {
                materials.add(invState);
            }
        }

        WarehouseInventoryState state = new WarehouseInventoryState(wState, products, materials);

        return state;
    }

    @Override
    public Long createDeliveryOrder(Long salesOrderId, Long clientId, Long contactId, Date dateIssued, List<LineEntryState> lineEntryStateList,
            Long billingId, Long shippingId, String description, String documentName,
            String shippingMethod, String shippingRule, String paymentMethod, String freightCostRule, String pricingType) {

        System.out.println("addDeliveryOrder");
        Long returnId = Long.parseLong("0");

        SalesOrderEntity so = em.find(SalesOrderEntity.class, salesOrderId);
        if (so != null) {
            DeliveryOrderEntity doe = new DeliveryOrderEntity();


            Calendar cal = Calendar.getInstance();
            cal.setTime(so.getDateIssued());
            cal.add(Calendar.DATE, so.getleadTimeConfirmed());
            Date dateDue = cal.getTime();

            cal.setTime(dateDue);
            cal.add(Calendar.DATE, -3);
            Date checkBy = cal.getTime();

            double finalPrice = 0;
            List<LineEntryEntity> lineEntryList = new ArrayList<LineEntryEntity>();
            Collection<LineEntryEntity> oriCol = so.getLineEntryList();
            List<LineEntryEntity> oriList = new ArrayList<LineEntryEntity>(oriCol);
            for (Object o : lineEntryStateList) {
                //ProductEntity product1=(ProductEntity) o;
                LineEntryState line = (LineEntryState) o;
                System.out.println("Find product");
                ProductEntity product = em.find(ProductEntity.class, line.getItemId());
                String itemName = "";
                double basePrice = 0;
                double listprice = 0;
                if (product != null) {
                    System.out.println("If product not null");
                    itemName = product.getName();
                    basePrice = product.getBasePrice();
                    //listprice = product.getListPrice();
                }
                int quantity = line.getQuantity();
                double price = line.getPrice();
                Long itemId = line.getItemId();

                //lineEntryObject.setItemName(itemName);
                LineEntryEntity lineEntry = new LineEntryEntity();
                System.out.println("Create line entry object");
                lineEntry.create(itemId, itemName, quantity, basePrice, price, product);
                lineEntry.setTotalLinePrice(line.getTotalPrice());
                finalPrice = finalPrice + line.getTotalPrice();
                lineEntry.setPricebookId(line.getPricebookId());
                lineEntryList.add(lineEntry);
                System.out.println("Persist line entry object");
                em.persist(lineEntry);
                em.flush();

                for (Object p : oriList) {
                    LineEntryEntity l = (LineEntryEntity) p;
                    if (l.getItemId().equals(itemId)) {
                        int amtInDO = l.getAmountInDeliveryOrder();
                        l.setAmountInDeliveryOrder(amtInDO + quantity);
                        em.flush();
                        break;
                    }
                }
            }


            BillingAddressEntity bAddress = em.find(BillingAddressEntity.class, billingId);
            ShippingAddressEntity sAddress = em.find(ShippingAddressEntity.class, shippingId);

            CustomerEntity customer = em.find(CustomerEntity.class, clientId);
            ContactEntity contact = em.find(ContactEntity.class, contactId);

            System.out.println("Create delivery order");
            doe.create(dateIssued, dateDue, checkBy,
                    shippingMethod, shippingRule, freightCostRule,
                    lineEntryList, bAddress, sAddress, customer);
            em.persist(doe);
            doe.setContact(contact);
            doe.setPaymentMethod(paymentMethod);
            doe.setPricingType(pricingType);
            doe.setStatus("Open");
            doe.setDeleteFlag("N");
            doe.setDescription(description);
            doe.setDocumentName(documentName);
            doe.setSalesOrder(so);
            doe.setTotalPrice(finalPrice);
            so.getDeliveryOrderList().add(doe);
            em.flush();
            returnId = doe.getId();

        }

        return returnId;
    }

    public void updateDeliveryOrder(Long doId, Long salesOrderId, Long clientId, Long contactId, Date dateIssued,
            String description, String documentName, String shippingMethod,
            String shippingRule, String paymentMethod, String freightCostRule,
            String pricingType, String status, Long billingId, Long shippingId) {


        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, doId);
        if (doe != null) {
            CustomerEntity customer = em.find(CustomerEntity.class, clientId);
            doe.setCustomer(customer);
            ContactEntity contact = em.find(ContactEntity.class, contactId);
            doe.setContact(contact);
            BillingAddressEntity ba = new BillingAddressEntity();
            ShippingAddressEntity sa = new ShippingAddressEntity();
            if (billingId != 0) {
                ba = em.find(BillingAddressEntity.class, billingId);
                if (ba != null) {
                    doe.setBillingAddress(ba);
                }
            }
            if (shippingId != 0) {
                sa = em.find(ShippingAddressEntity.class, shippingId);
                if (sa != null) {
                    doe.setShippingAddress(sa);
                }
            }

            doe.setDescription(description);
            doe.setDocumentName(documentName);
            doe.setShippingMethod(shippingMethod);
            doe.setShippingRule(shippingRule);
            doe.setPaymentMethod(paymentMethod);
            doe.setFreightCostRule(freightCostRule);
            doe.setPricingType(pricingType);
            if(status.equals("Voided")){
                withdrawDeliveryOrder(doe.getId());
            }
            em.flush();
        }
    }

    @Override
    public void executeDeliveryOrder(Long doId, String shippingCompany) {
        System.out.println("Execute delivery order");

        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, doId);

        if (doe == null) {
            return;
        }

        SalesOrderEntity salesOrder = doe.getSalesOrder();
        Collection<LineEntryEntity> soList = salesOrder.getLineEntryList();
        Collection<LineEntryEntity> doList = doe.getLineEntryList();

        System.out.println("Setting amount invoice in sales and delivery order!");
        for (Object d : doList) {
            LineEntryEntity deliveryLine = (LineEntryEntity) d;
            for (Object s : soList) {
                LineEntryEntity soLine = (LineEntryEntity) s;
                if (deliveryLine.getItemId().equals(soLine.getItemId())) {
                    soLine.setAmountInvoiced(deliveryLine.getQuantity());
                    deliveryLine.setAmountShipped(deliveryLine.getQuantity());
                    em.flush();
                }
            }
        }

        doe.setExecuted(true);
        doe.setStatus("Shipped");

        em.flush();

        System.out.println("Sending notification to Customer Executive");
        String role = "Customer Executive";
        String title = "Ready To Ship: DO" + doId;
        String body = "Message from SHIPPING DEPARTMENT to SALES DEPARTMENT" + "\n\n"
                + "Shipping request has been executed for the following sales document: " + "\n"
                + "==================================" + "\n"
                + "Sales Order ID      : " + doe.getSalesOrder().getId() + "\n"
                + "Client ID           : " + doe.getCustomer().getId() + "\n"
                + "==================================" + "\n\n\n"
                + "Please follow up with the Finance Department to invoice client. \n\n"
                + "Thank you.";


        messenger.addMessage(role, title, body);

    }

    @Override
    public void withdrawDeliveryOrder(Long doId) {
        System.out.println("Withdraw delivery order");

        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, doId);

        if (doe != null) {
            System.out.println("DOE NOT NULL");
            PickingOrderEntity poe = doe.getPickingOrderEntity();

            if (poe != null) {
                this.voidPickingOrder(poe);
            }
            
            doe.setStatus("Voided");
            em.flush();
      
            for (Object o : doe.getLineEntryList()) {
                LineEntryEntity l = (LineEntryEntity) o;
                l.setDeleteFlag("Y");
            }
            em.flush();
        }

    }
    
     public void deleteDeliveryOrder(Long doId) {
        System.out.println("Delete delivery order");

        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, doId);

        if (doe != null) {
            
            PickingOrderEntity poe = doe.getPickingOrderEntity();

            if (poe != null) {
                this.voidPickingOrder(poe);
            }
            
            doe.setDeleteFlag("Y");
            em.flush();
            for (Object o : doe.getLineEntryList()) {
                LineEntryEntity l = (LineEntryEntity) o;
                l.setDeleteFlag("Y");
            }
            em.flush();
        }

    }

    public DeliveryOrderState searchDeliveryOrder(Long id) {

        // Query q = em.createQuery("Select a FROM DeliveryOrderEntity a WHERE a.id=:id").setParameter("id", id);
        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, id);

        DeliveryOrderState doState = new DeliveryOrderState(doe.getId(),
                doe.getSalesOrder().getId(), doe.getStatus(),
                doe.getShippingMethod(), doe.getShippingRule(),
                doe.getShippingCompany(), doe.getCustomer().getId(),
                doe.getCustomer().getName(), doe.getContact().getId(),
                doe.getContact().getContactNum(), doe.getDeleteFlag(),
                doe.getDueDate(), doe.getDateIssued(), doe.getCheckBy(),
                doe.getFreightCostRule(), doe.getFreightCost(),
                doe.getTotalPrice(), doe.getDescription(), doe.getPricingType(),
                doe.getPaymentMethod(), doe.getBillingAddress().getId(),
                doe.getShippingAddress().getId(), doe.getDocumentName(), doe.getLineEntryList());

        BillingAddressEntity ba = doe.getBillingAddress();
        doState.setBillingId(ba.getId());
        doState.setBillingStreet(ba.getBillingStreet());
        doState.setBillingState(ba.getBillingState());
        doState.setBillingCity(ba.getBillingCity());
        doState.setBillingCode(ba.getBillingCode());
        doState.setBillingCountry(ba.getBillingCountry());

        ShippingAddressEntity sa = doe.getShippingAddress();
        doState.setShippingId(sa.getId());
        doState.setShippingStreet(sa.getShippingStreet());
        doState.setShippingState(sa.getShippingState());
        doState.setShippingCity(sa.getShippingCity());
        doState.setShippingCode(sa.getShippingCode());
        doState.setShippingCountry(sa.getShippingCountry());

        return doState;
    }

    protected double round(double d) {

        DecimalFormat twoDForm = new DecimalFormat("#.##");
        return Double.valueOf(twoDForm.format(d));
    }

    public List<DeliveryOrderState> viewDeliveryOrders() {

        System.out.println("viewDeliveryOrders");

        Query q = em.createQuery("SELECT c FROM DeliveryOrderEntity c");
        List<DeliveryOrderState> stateList = new ArrayList();
        double totalPrice = 0;
        for (Object o : q.getResultList()) {
            DeliveryOrderEntity c = (DeliveryOrderEntity) o;
            if (!c.getDeleteFlag().equals("Y") /*&& smb.checkClientStatus(c.getCustomer())*/) {

                List<LineEntryEntity> lineEntryList = c.getLineEntryList();
                for (Object l : lineEntryList) {
                    LineEntryEntity lineEntry = (LineEntryEntity) l;
                    totalPrice = totalPrice + lineEntry.getTotalLinePrice();
                }

                DeliveryOrderState doState = new DeliveryOrderState(c.getId(), c.getSalesOrder().getId(),
                        c.getStatus(), c.getShippingMethod(), c.getShippingRule(),
                        c.getDueDate(), c.getDateIssued(), c.getCheckBy(),
                        c.getFreightCostRule());

                doState.setTotalPrice(round(totalPrice));
                doState.setPricingType(c.getPricingType());
                doState.setPaymentMethod(c.getPaymentMethod());
                doState.setDescription(c.getDescription());
                doState.setClientId(c.getCustomer().getId());
                doState.setClientName(c.getCustomer().getName());

                doState.setDocumentName(c.getDocumentName());
                if (c.getContact() != null) {
                    doState.setContactId(c.getContact().getId());
                    doState.setContactName(c.getContact().getFirstName() + c.getContact().getLastName());
                } else {
                    doState.setContactId(Long.parseLong("0"));
                    doState.setContactName("Not yet specified");
                }

                BillingAddressEntity ba = c.getBillingAddress();
                doState.setBillingId(ba.getId());
                doState.setBillingStreet(ba.getBillingStreet());
                doState.setBillingState(ba.getBillingState());
                doState.setBillingCity(ba.getBillingCity());
                doState.setBillingCode(ba.getBillingCode());
                doState.setBillingCountry(ba.getBillingCountry());

                ShippingAddressEntity sa = c.getShippingAddress();
                doState.setShippingId(sa.getId());
                doState.setShippingStreet(sa.getShippingStreet());
                doState.setShippingState(sa.getShippingState());
                doState.setShippingCity(sa.getShippingCity());
                doState.setShippingCode(sa.getShippingCode());
                doState.setShippingCountry(sa.getShippingCountry());

                stateList.add(doState);
            }

        }
        return stateList;
    }

    public void editPickingOrderEntry(Long id, String status, Long warehouseId, Date pickingDate) {
        System.out.println("Edit picking order entry");
        PickingOrderEntryEntity poe = em.find(PickingOrderEntryEntity.class, id);

        if (poe != null) {

            System.out.println("Picking order found!");
            poe.setStatus(status);
            poe.setWarehouseId(warehouseId);


            poe.setPickingDate(pickingDate);

            em.flush();
            System.out.println("Picking order edited");
        }

    }

    public void updatePickingOrder(Long id, String pickingRoute,
            String pickingMethod, Date issueDate, Date dueDate,
            Long collectionWarehouseId, String status) {

        PickingOrderEntity p = em.find(PickingOrderEntity.class, id);

        if (p != null) {
            System.out.println("Picking order found.");
            p.setPickingRoute(pickingRoute);
            p.setPickingMethod(pickingMethod);
            p.setIssueDate(issueDate);
            p.setDueDate(dueDate);
            p.setCollectionWarehouseId(collectionWarehouseId);
            if (status.equals("Voided")) {
                this.voidPickingOrder(p);
            }else if(status.equals("Picked")){
                p.getDeliveryOrder().setStatus("Shipped");
            }
            p.setStatus(status);
            em.flush();
            System.out.println("Picking order edited.");
        }

    }

    @Override
    public PickingOrderEntity createPickingOrder(Long doId, String pickingRoute,
            String pickingMethod, Date issueDate, Date dueDate, Long collectionWarehouseId, int shipGroup) {
        //shipGroup = 1 or 2
        DeliveryOrderEntity doe = em.find(DeliveryOrderEntity.class, doId);

        String status = "In Progress";

        if (doe == null) {
            throw new NullPointerException("Invalid Delivery Order specified");
        }

        PickingOrderEntity poe = new PickingOrderEntity(doe, status, pickingRoute, pickingMethod, issueDate, dueDate, collectionWarehouseId);
        em.persist(poe);


        List<LineEntryEntity> doeEntries = new ArrayList();
        doeEntries = doe.getLineEntryList();

        SalesDocumentEntity sd = doe.getSalesOrder();
        System.out.println("Fetching " + sd.getDocumentType() + sd.getId());
        List<ReservationState> reservations = this.viewUnpickedReservationsOnSalesOrder(sd.getId());

        Collection<PickingOrderEntryEntity> poeEntries = new ArrayList<PickingOrderEntryEntity>();

        for (Object o : doeEntries) {
            LineEntryEntity doeEntry = (LineEntryEntity) o;

            int requiredQty = doeEntry.getQuantity();
            System.out.println("Req quantity: " + requiredQty);
            for (ReservationState rState : reservations) {
                System.out.println("Processing reservation ID:" + rState.getId());
                ReservationEntity rEntity = em.find(ReservationEntity.class, rState.getId());

                PickingOrderEntryEntity poeEntry = new PickingOrderEntryEntity();
                poeEntry.create(rState.getProductId(), requiredQty, rState.getQuantity(),
                        rState.getReservationType(),
                        rState.getAvailabilityDate(), false, false, rState.getWarehouseId(), rEntity);

                em.persist(poeEntry);
                poeEntry.setStatus("Pending");
                rEntity.setPicked(true);
                poeEntries.add(poeEntry);
                em.flush();
            }
        }
        System.out.println("Set entries");
        poe.setEntries(poeEntries);
        em.flush();
        if (poe.getEntries() != null) {
            System.out.println("entries not null!");
        }
        poe.setDeliveryOrder(doe);
        doe.setPickingOrderEntity(poe);
        em.flush();
        return poe;
    }

    public void removePickingOrderEntryFromPickingList(Long poeId, Long entryId) {
        PickingOrderEntity poe = em.find(PickingOrderEntity.class, poeId);
        PickingOrderEntryEntity entry = em.find(PickingOrderEntryEntity.class, entryId);

        List<PickingOrderEntryEntity> entries = new ArrayList();
        entries = (List<PickingOrderEntryEntity>) poe.getEntries();

        entries.remove(entry);
        poe.setEntries(entries);

        entry.getReservationEntity().setPicked(false); //making the entry available for another picking round
        entry.setReservationEntity(null);
        entry.setStatus("Picked");
        em.flush();
    }

    public void executePickingOrder(Long poeId) {
        System.out.println("EJB::Execute picking order");
        PickingOrderEntity poe = em.find(PickingOrderEntity.class, poeId);


        Collection<PickingOrderEntryEntity> colEntries = poe.getEntries();
        List<PickingOrderEntryEntity> entries = new ArrayList<PickingOrderEntryEntity>(colEntries);
        Long productId = entries.get(0).getProductId();
        Long warehouseId = entries.get(0).getWarehouseId();
        int aggregate = 0;

        for (PickingOrderEntryEntity entry : entries) {
            System.out.println("For each picking order entry");
            ProductEntity product = em.find(ProductEntity.class, entry.getProductId());
            WarehouseEntity warehouse = em.find(WarehouseEntity.class, entry.getWarehouseId());
            product.setReservedQty(product.getReservedQty() - entry.getPickingQty());
            entry.setPicked(true);
            entry.setStatus("Picked");
            aggregate += entry.getPickingQty();
            if (!warehouseId.equals(entry.getWarehouseId()) || !productId.equals(entry.getProductId())) {
                InventoryMovementsRecordEntity log = new InventoryMovementsRecordEntity();
                log.createLog(product.getId(), product.getName(), aggregate, "OUTBOUND", warehouseId, warehouse.getName());
                em.persist(log);
                aggregate = 0;
                warehouseId = entry.getWarehouseId();
                productId = entry.getProductId();
            }

            em.flush();
        }
        poe.setStatus("Picked");
        poe.getDeliveryOrder().setStatus("Picked");
        em.flush();
    }

    //to be called by withdraw delivery order
    protected void voidPickingOrder(PickingOrderEntity poe) {
        System.out.println("Void Picking order");
        Collection<PickingOrderEntryEntity> entries = poe.getEntries();
       // entries = (List<PickingOrderEntryEntity>) poe.getEntries();

        for (Object o : entries) {
            PickingOrderEntryEntity entry = (PickingOrderEntryEntity) o;
            entry.setVoided(true);
          //  entries.remove(entry);
            em.flush();
        }

        poe.setEntries(null);
        poe.setDeleted(true);

        em.flush();
    }

    //to be integrated with Picking Order page
    protected List<ReservationState> viewUnpickedReservationsOnSalesOrder(Long salesOrderId) {
        List<ReservationState> states = pmb.viewReservationsOnSalesDocument(salesOrderId);
        List<ReservationState> results = new ArrayList();
        int n = 0;
        for (ReservationState state : states) {
            if (!state.isPicked() && !state.isVoided()) {
                results.add(state);
                n++;
            }
        }
        System.out.println("no of reservations for this SD: " + n);
        return results;
    }

    //manual withdrawal of picking order
    public void withdrawPickingOrder(Long id) {
        System.out.println("Delete picking order");

        PickingOrderEntity p = em.find(PickingOrderEntity.class, id);
        if (p != null) {
            System.out.println("Picking order found.");
            p.setDeleted(true);

            System.out.println("Find picking order entries.");
            Collection<PickingOrderEntryEntity> pickList = p.getEntries();
            for (Object o : pickList) {
                PickingOrderEntryEntity po = (PickingOrderEntryEntity) o;
                po.setStatus("Deleted");
                em.flush();
            }

            em.flush();
            System.out.println("Picking Order Deleted");
        }
    }

    public List<PickingOrderState> viewPickingOrders() {
        System.out.println("viewPickingOrders");

        Query q = em.createQuery("Select a from PickingOrderEntity a");
        List<PickingOrderState> stateList = new ArrayList<PickingOrderState>();

        for (Object o : q.getResultList()) {
            PickingOrderEntity p = (PickingOrderEntity) o;
            if (!p.isDeleted()) {
                Long targetWarehouseId = Long.parseLong("0");
                String warehouseName = "";
                if (p.getCollectionWarehouseId() != null) {
                    targetWarehouseId = p.getCollectionWarehouseId();
                    WarehouseEntity w = em.find(WarehouseEntity.class, targetWarehouseId);
                    warehouseName = w.getName();
                }
                System.out.println(p.getId());
                System.out.println(p.getDeliveryOrder().getId());
                System.out.println(p.getPickingRoute());
                System.out.println(p.getPickingMethod());
                System.out.println(p.getIssueDate());
                System.out.println(p.getDueDate());
                System.out.println(targetWarehouseId);
                System.out.println(p.isDeleted());

                PickingOrderState poState = new PickingOrderState(p.getId(), p.getDeliveryOrder().getId(),
                        p.getPickingRoute(), p.getPickingMethod(), p.getIssueDate(), p.getDueDate(),
                        targetWarehouseId, p.getStatus(), p.isDeleted());
                poState.setTargetWarehouseName(warehouseName);
                stateList.add(poState);
            }
        }

        return stateList;
    }

    public List<PickingOrderEntryState> viewPickingOrderEntries(Long id) {

        PickingOrderEntity po = em.find(PickingOrderEntity.class, id);
        List<PickingOrderEntryState> entryList = new ArrayList<PickingOrderEntryState>();
        if (po != null) {
            System.out.println("Picking order found!");
            Collection<PickingOrderEntryEntity> pickList = po.getEntries();
            if (pickList != null && !pickList.isEmpty()) {
                System.out.println("not null");
            }
            for (Object o : pickList) {
                PickingOrderEntryEntity p = (PickingOrderEntryEntity) o;
                PickingOrderEntryState peState = new PickingOrderEntryState();
                String reservationType = "";
                String availabilityDate = "";
                String status = "";
                Date pickingDate = new Date();
                pickingDate = p.getPickingDate();
                if (pickingDate != null) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(pickingDate);
                    cal.add(Calendar.DATE, 1);
                    pickingDate = cal.getTime();
                }

                ReservationEntity r = p.getReservationEntity();
                Long warehouseId = p.getWarehouseId();
                WarehouseEntity w = em.find(WarehouseEntity.class, warehouseId);

                String warehouseName = "";

                if (w != null) {
                    warehouseName = w.getName();
                } else {
                    warehouseName = "";
                }

                /*System.out.println(p.getId()+" "+p.getProductId()+ " "+
                p.getRequestedQty()+" "+ p.getPickingQty()+ " "+
                reservationType+ " "+
                availabilityDate+ " "+
                status+" "+ p.getWarehouseId()+ "picDate"+" "+ 
                ids);*/
                peState.create(p.getId(), p.getProductId(),
                        p.getRequestedQty(), p.getPickingQty(),
                        p.getReservationType(),
                        p.getAvailabilityDate(), p.isVoided(), p.isPicked(),
                        p.getStatus(), p.getWarehouseId(), pickingDate,
                        r.getId());
                peState.setWarehouseName(warehouseName);

                entryList.add(peState);
            }
        }

        return entryList;
    }

    @Override
    public PickingOrderState searchPickingOrder() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public List<InventoryMovementState> viewInventoryMovementLog() {
        Query q = em.createQuery("SELECT a FROM InventoryMovementsRecordEntity a");
        List<InventoryMovementState> states = new ArrayList();

        for (Object o : q.getResultList()) {
            InventoryMovementsRecordEntity log = (InventoryMovementsRecordEntity) o;

            InventoryMovementState state = new InventoryMovementState(log.getId(), log.getItemId(), log.getItemName(), log.getQty(), log.getDirection(), log.getWarehouseId(), log.getWarehouseName(), log.getMovementDate());
            states.add(state);
        }

        return states;

    }
}
